////////////////////////////////////////////////////////////////////////////
//  Copyright 2009 - 2011 Zach Snow                                       //
////////////////////////////////////////////////////////////////////////////
//  This file is part of WindowsWhere.                                    //
//                                                                        //
//  WindowsWhere is free software: you can redistribute it and/or modify  //
//  it under the terms of the GNU General Public License as published by  //
//  the Free Software Foundation, either version 3 of the License, or     //
//  (at your option) any later version.                                   //
//                                                                        //
//  WindowsWhere is distributed in the hope that it will be useful,       //
//  but WITHOUT ANY WARRANTY; without even the implied warranty of        //
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         //
//  GNU General Public License for more details.                          //
//                                                                        //
//  You should have received a copy of the GNU General Public License     //
//  along with WindowsWhere.  If not, see <http://www.gnu.org/licenses/>. //
////////////////////////////////////////////////////////////////////////////
//  Parts of this file were derived from Win32.cs, by Chris Wilson.       //
////////////////////////////////////////////////////////////////////////////
using System;
using System.Text;
using System.Runtime.InteropServices;
using System.Drawing;

namespace WindowsWhere
{
  /// <summary>
  /// Contains possibly wrapped Win32 API calls; this class is used
  /// throughout WindowsWhere to access the Win32 API.  Functions are
  /// wrapped so that they don't require ref or out parameters or
  /// StringBuilders, and so that they throw reasonable exceptions when
  /// the underlying call fails.
  /// </summary>
	public class Win32
	{
		public delegate bool EnumWindowDelegate(IntPtr hwnd, int lParam);

    private const uint MAX_LENGTH = 1024;

		[DllImport("user32.dll")]
		public static extern int EnumWindows(EnumWindowDelegate x, int y);
		[DllImport("user32.dll")]
		public static extern IntPtr GetWindow(IntPtr hWnd, int uCmd);

    [DllImport("user32.dll")]
    private static extern int GetWindowText(IntPtr hWnd, StringBuilder title, int size);

    public static string GetWindowText(IntPtr hWnd)
    {
      StringBuilder s = new StringBuilder((int)MAX_LENGTH);
      GetWindowText(hWnd, s, (int)MAX_LENGTH);
      return s.ToString();
    }

		[DllImport("user32.dll")]
		public static extern bool IsWindowVisible(IntPtr hWnd);
		[DllImport("user32.dll")]
		public static extern int RegisterWindowMessage(string lpString);
		[DllImport("user32.dll")]
		public static extern IntPtr GetSystemMenu(IntPtr hWnd, bool Revert);
		[DllImport("user32.dll")]
		public static extern bool InsertMenu(IntPtr hMenu, int uPosition, int uFlags, IntPtr uIDNewItem, string lpNewItem);
		[DllImport("user32.dll")]
		public static extern bool InsertMenu(IntPtr hMenu, int uPosition, int uFlags, int uIDNewItem, string lpNewItem);
		[DllImport("user32.dll")]
		public static extern bool RemoveMenu(IntPtr hMenu, int uPosition, int uFlags);
		[DllImport("user32.dll")]
		public static extern int GetMenuItemCount(IntPtr hMenu);
		[DllImport("user32.dll")]
		public static extern bool SetLayeredWindowAttributes(IntPtr hwnd, uint crKey, byte bAlpha, int dwFlags);
		[DllImport("user32.dll")]
		public static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);
		[DllImport("user32.dll")]
		public static extern int GetWindowLong(IntPtr hWnd, int nIndex);
		[DllImport("user32.dll")]
		public static extern IntPtr CreateMenu();
		[DllImport("user32.dll")]
		public static extern bool DestroyMenu(IntPtr hMenu);
    [DllImport("user32.dll")]
    public static extern bool ShowWindow(IntPtr hwnd, int nCmdShow);

    [DllImport("user32.dll")]
    private static extern uint GetClassName(IntPtr hWnd, StringBuilder pszType, uint cchType);

    public static string GetWindowClass(IntPtr hWnd)
    {
      StringBuilder s = new StringBuilder((int)MAX_LENGTH);
      GetClassName(hWnd, s, MAX_LENGTH);
      return s.ToString();
    }

    #region Window Rectangle
    [DllImport("user32.dll")]
    private static extern bool GetWindowRect(IntPtr hWnd, out RECT rect);

    [StructLayout(LayoutKind.Sequential)]
    private struct RECT
    {
      public int Left;
      public int Top;
      public int Right;
      public int Bottom;
    }

    public static Rectangle GetWindowRectangle(IntPtr handle)
    {
      RECT rect;

      if(!GetWindowRect(handle, out rect))
      {
        throw new InvalidOperationException("Window has no rectangle");
      }
      else
      {
        return new Rectangle(rect.Left, rect.Top, rect.Right - rect.Left, rect.Bottom - rect.Top);
      }
    }

    [DllImport("user32.dll")]
    private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);
    const UInt32 SWP_NOSIZE         =0x0001;
    const UInt32 SWP_NOMOVE         =0x0002;
    const UInt32 SWP_NOZORDER       =0x0004;
    const UInt32 SWP_NOREDRAW       =0x0008;
    const UInt32 SWP_NOACTIVATE     =0x0010;
    const UInt32 SWP_FRAMECHANGED   =0x0020;
    const UInt32 SWP_SHOWWINDOW     =0x0040;
    const UInt32 SWP_HIDEWINDOW     =0x0080;
    const UInt32 SWP_NOCOPYBITS     =0x0100;
    const UInt32 SWP_NOOWNERZORDER  =0x0200;
    const UInt32 SWP_NOSENDCHANGING =0x0400;
    const UInt32 SWP_DRAWFRAME      =SWP_FRAMECHANGED;
    const UInt32 SWP_NOREPOSITION   =SWP_NOOWNERZORDER;
    const UInt32 SWP_DEFERERASE     =0x2000;
    const UInt32 SWP_ASYNCWINDOWPOS =0x4000;

    public static void SetWindowRectangle(IntPtr handle, Rectangle r)
    {
      UInt32 flags = SWP_NOZORDER | SWP_NOACTIVATE | SWP_ASYNCWINDOWPOS;
      if(!SetWindowPos(handle, handle, r.Left, r.Top, r.Width, r.Height, flags))
      {
        throw new InvalidOperationException("Unable to set window position");
      }
      return;
    }
    #endregion

    // Menus
		public static int MF_STRING = 0x00000000;
		public static int MF_POPUP = 0x00000010;
		public static int MF_BYCOMMAND = 0x00000000;
		public static int MF_BYPOSITION = 0x00000400;
		public static int MF_SEPARATOR = 0x00000800;

		// GetWindow
		public static int GW_OWNER = 4;

		// LayeredWindowAttributes
		public static int LWA_COLORKEY = 0x00000001;
		public static int LWA_ALPHA = 0x00000002;

		// WindowLong
		public static int GWL_WNDPROC = (-4);
		public static int GWL_HINSTANCE = (-6);
		public static int GWL_HWNDPARENT = (-8);
		public static int GWL_STYLE = (-16);
		public static int GWL_EXSTYLE = (-20);
		public static int GWL_USERDATA = (-21);
		public static int GWL_ID = (-12);

    // ShowWindow
    public static int SW_RESTORE = 9;

		// WindowStyle
		public static int WS_EX_LAYERED = 0x00080000;

		// Window Messages
		public static int WM_CREATE = 0x0001;
		public static int WM_DESTROY = 0x0002;
		public static int WM_MOVE = 0x0003;
		public static int WM_SIZE = 0x0005;
		public static int WM_ACTIVATE = 0x0006;
    public static int WM_KEYDOWN = 0x0100;
		public static int WM_COMMAND = 0x0111;
		public static int WM_SYSCOMMAND = 0x0112;
		public static int WM_MENUCOMMAND = 0x0126;
		public static int WM_MENUSELECT = 0x011F;
	}
}
